<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
    <style>
        html,
        body {
            margin: 0;
            height: 100%;
            background: black;
        }

        canvas,
        .overlay,
        input {
            position: absolute;
        }

        canvas {
            z-index: 1;
        }

        .hidden {
            display: none;
        }

        .overlay {
            z-index: 10;
        }

        input {
            border-radius: 5px;
            width: 99%;
            height: 50px;
            top: 5px;
            left: 0.5%;
            opacity: 0.8;
            font-size: 26px;
            text-indent: 1em;
        }
    </style>

    <script src='//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.2/underscore-min.js'></script>
</head>
<body>
<canvas class="hidden" id="text_canvas"></canvas>
<canvas id="canvas"></canvas>
<input id="thingy" type="text" class="overlay" placeholder="Type here"/>

</body>
</html>
<script>

    'use strict';

    var particlePrototype = {
        x: 0,
        y: 0,
        r: 1,
        color: '#FFF',
        vector: false,
        target: false,
        simulate: function simulate() {
            if (!this.vector || !this.target) {
                return false;
            }

            if (this.isArrived()) {
                this.target = false;
                this.vector = false;
            } else {
                this.move(this.vector);
            }

            return true;
        },
        isArrived: function isArrived() {
            var difX = Math.abs(this.target.x - this.x),
                    difY = Math.abs(this.target.y - this.y),
                    threshold = 1;
            return !(difX > threshold || difY > threshold);
        },
        setVector: function setVector(vector) {
            this.vector = vector;
        },
        setTarget: function setTarget(target) {
            this.target = target;
        },
        move: function move(vector) {
            this.x -= vector.x;
            this.y -= vector.y;
        },
        draw: function draw(ctx) {
            ctx.strokeStyle = this.color;
            ctx.beginPath();
            ctx.arc(this.x, this.y, this.r, 0, 2 * Math.PI);
            ctx.stroke();
        }
    };

    function particle(options) {
        return _.extend(Object.create(particlePrototype), options);
    }

    function vector(origin, target) {
        var x = origin.x - target.x,
                y = origin.y - target.y,
                length = Math.sqrt(x * x + y * y);

        return {
            x: x / length,
            y: y / length
        };
    }

    function randCoord(maxX, maxY) {
        return {
            x: Math.floor(Math.random() * maxX),
            y: Math.floor(Math.random() * maxY)
        };
    }

    function randomColor() {
        var letters = '0123456789ABCDEF'.split('');
        var color = '#';
        for (var i = 0; i < 6; i++) {
            color += letters[Math.floor(Math.random() * 16)];
        }
        return color;
    }

    function scaleCanvas() {
        canvas.width = w;
        canvas.height = h;
        text_canvas.width = w;
        text_canvas.height = h;
    }

    function generateParticles() {
        for (var i = 0; i < config.particleCount; i++) {
            var pos = randCoord(w, h);
            particles.push(particle({
                x: pos.x,
                y: pos.y,
                color: randomColor()
            }));
        }
    }

    function drawParticles() {
        particles.forEach(function (particle) {
            particle.draw(ctx);
        });
    }

    function render() {
        ctx.fillStyle = 'rgba(0,0,0,0.2)';
        ctx.fillRect(0, 0, w, h);
// 		canvas.width = canvas.width;
        simulate();
        drawParticles();

        if (!done) {
            requestAnimationFrame(render);
        }
    }

    function attachEvents() {
        input.addEventListener('input', inputChangeHandler);
    }

    function inputChangeHandler(event) {
        newText(event.target.value);
    }

    function newText(newText) {
        text = newText;
        text_canvas.width = text_canvas.width;

        if (text.length === 0) {
            assignEmptyTargets();
        } else {
            fitTextOnCanvas(text_ctx, text);
            textPositions = getTextPositions(text_ctx);
            assignTextTargets();
        }

        if (done) {
            done = false;
            requestAnimationFrame(render);
        }
    }

    function assignEmptyTargets() {
        particles.forEach(function (particle) {
            var target = randCoord(w, h);
            particle.setTarget(target);
            particle.setVector(vector(particle, target));
        });
    }

    function assignTextTargets() {
        particles.forEach(function (particle) {
            var target = getRandomTextTarget();
            particle.setTarget(target);
            particle.setVector(vector(particle, target));
        });
    }

    function getRandomTextTarget() {
        return textPositions[Math.floor(Math.random() * textPositions.length)];
    }

    function simulate() {
        done = true;
        particles.forEach(function (particle) {
            if (particle.simulate()) {
                done = false;
            }
        });
    }

    function fitTextOnCanvas(ctx, text) {
// http://stackoverflow.com/questions/20551534/size-to-fit-font-on-a-canvas
        config.adjustedFontSize = config.baseFontSize;
// lower the font size until the text fits the canvas
        do {
            config.adjustedFontSize--;
            ctx.font = config.adjustedFontSize + 'px ' + config.fontFace;
        } while (ctx.measureText(text).width > w);

// draw the text
        ctx.fillStyle = '#FFF';
        ctx.textAlign = 'center';
        ctx.textBaseline = 'middle';
        ctx.fillText(text, w / 2, h / 2, w);
    }

    function getTextPositions(ctx) {
//scan every single pixel and check if it's white.
//yes, really
        var pixelData = ctx.getImageData(0, 0, w, h);

        var temp = [];

//TODO, only scan the region possible to have text in it
        for (var x = 0; x < w; x++) {
            for (var y = 0; y < h; y++) {
                if (isPixelWhite(pixelData, x, y)) {
                    temp.push({
                        x: x,
                        y: y
                    });
                }
            }
        }

        return temp;
    }

    function isPixelWhite(pixelData, x, y) {
        var index = (x + y * pixelData.width) * 4;
        var r = pixelData.data[index + 0];
        var g = pixelData.data[index + 1];
        var b = pixelData.data[index + 2];
        var a = pixelData.data[index + 3];

        return r + g + b + a === 1020;
    }

    var canvas = document.getElementById('canvas'),
            done = true,
            textCanvas = document.getElementById('text_canvas'),
            ctx = ctx = canvas.getContext('2d'),
            text_ctx = text_canvas.getContext('2d'),
            h = document.body.clientHeight,
            w = document.body.clientWidth,
            a = w * h,
            config = {
                fontFace: 'Comic Sans, Arial',
                particleCount: a * 0.001, // / window.devicePixelRatio,
                baseFontSize: 700,
                adjustedFontSize: 700
            },
            particles = [],
            textPositions = [],
            input = document.getElementById('thingy'),
            text = '↑ Type in the box';

    scaleCanvas();
    generateParticles();
    attachEvents();

    newText(text);
    // render();
</script>